﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Go;

namespace fhs
{
    class McOption
    {
        const int mctimeout = 30000;
        generator _mcAction;
        csp_chan<ushort, int> _readCsp;
        csp_chan<uint, int> _readCsp32;
        csp_chan<ushort[], tuple<int, int>> _readsCsp;
        csp_chan<uint[], tuple<int, int>> _readsCsp32;
        csp_chan<void_type, tuple<int, ushort[]>> _readsCspBuf;
        csp_chan<void_type, tuple<int, uint[]>> _readsCspBuf32;
        csp_chan<void_type, tuple<int, ushort>> _writeCsp;
        csp_chan<void_type, tuple<int, uint>> _writeCsp32;
        csp_chan<void_type, tuple<int, ushort[]>> _writesCsp;
        csp_chan<void_type, tuple<int, uint[]>> _writesCsp32;

        public McOption(string ip, int port, int heartbeat)
        {
            _readCsp = new csp_chan<ushort, int>(GlobalData.mainStrand);
            _readCsp32 = new csp_chan<uint, int>(GlobalData.mainStrand);
            _readsCsp = new csp_chan<ushort[], tuple<int, int>>(GlobalData.mainStrand);
            _readsCsp32 = new csp_chan<uint[], tuple<int, int>>(GlobalData.mainStrand);
            _readsCspBuf = new csp_chan<void_type, tuple<int, ushort[]>>(GlobalData.mainStrand);
            _readsCspBuf32 = new csp_chan<void_type, tuple<int, uint[]>>(GlobalData.mainStrand);
            _writeCsp = new csp_chan<void_type, tuple<int, ushort>>(GlobalData.mainStrand);
            _writeCsp32 = new csp_chan<void_type, tuple<int, uint>>(GlobalData.mainStrand);
            _writesCsp = new csp_chan<void_type, tuple<int, ushort[]>>(GlobalData.mainStrand);
            _writesCsp32 = new csp_chan<void_type, tuple<int, uint[]>>(GlobalData.mainStrand);
            _mcAction = generator.tgo(GlobalData.mainStrand, functional.bind(McAction, ip, port, heartbeat));
        }

        private async Task McAction(string ip, int port, int heartbeat)
        {
            generator.children children = new generator.children();
            children.go(async delegate ()
            {
                while (true)
                {
                    await generator.sleep(100);
                    try
                    {
                        await Write(heartbeat, 0);
                    }
                    catch (chan_exception)
                    {
                        LogMgr.Error("PLC心跳信号异常");
                    }
                }
            });
            while (true)
            {
                mc_socket mc = new mc_socket(ip, port);
                try
                {
                    if (await mc.open())
                    {
                        LogMgr.Info($"PLC {ip}:{port} 连接成功");
                        await generator.select().case_receive(_readCsp, async delegate (int addr)
                        {
                            tuple<bool, ushort> res = await mc.read_single_word_d(addr);
                            if (!res.value1)
                            {
                                generator.select_chans.stop_all();
                            }
                            return res.value2;
                        }).case_receive(_readsCsp, async delegate (int addr, int count)
                        {
                            tuple<bool, ushort[]> res = await mc.read_multi_word_d(addr, count);
                            if (!res.value1)
                            {
                                generator.select_chans.stop_all();
                            }
                            return res.value2;
                        }).case_receive(_readsCspBuf, async delegate (int addr, ushort[] buf)
                        {
                            bool res = await mc.read_multi_word_d(addr, buf);
                            if (!res)
                            {
                                generator.select_chans.stop_all();
                            }
                        }).case_receive(_writeCsp, async delegate (int addr, ushort data)
                        {
                            if (!await mc.write_single_word_d(addr, data))
                            {
                                generator.select_chans.stop_all();
                            }
                        }).case_receive(_writesCsp, async delegate (int addr, ushort[] data)
                        {
                            if (!await mc.write_multi_word_d(addr, data))
                            {
                                generator.select_chans.stop_all();
                            }
                        }).case_receive(_readCsp32, async delegate (int addr)
                        {
                            tuple<bool, uint> res = await mc.read_single_dword_d(addr);
                            if (!res.value1)
                            {
                                generator.select_chans.stop_all();
                            }
                            return res.value2;
                        }).case_receive(_readsCsp32, async delegate (int addr, int count)
                        {
                            tuple<bool, uint[]> res = await mc.read_multi_dword_d(addr, count);
                            if (!res.value1)
                            {
                                generator.select_chans.stop_all();
                            }
                            return res.value2;
                        }).case_receive(_readsCspBuf32, async delegate (int addr, uint[] buf)
                        {
                            bool res = await mc.read_multi_dword_d(addr, buf);
                            if (!res)
                            {
                                generator.select_chans.stop_all();
                            }
                        }).case_receive(_writeCsp32, async delegate (int addr, uint data)
                        {
                            if (!await mc.write_single_dword_d(addr, data))
                            {
                                generator.select_chans.stop_all();
                            }
                        }).case_receive(_writesCsp32, async delegate (int addr, uint[] data)
                        {
                            if (!await mc.write_multi_dword_d(addr, data))
                            {
                                generator.select_chans.stop_all();
                            }
                        }).loop();
                        LogMgr.Error($"PLC {ip}:{port} 连接断开");
                    }
                    else
                    {
                        LogMgr.Error($"PLC {ip}:{port} 连接失败");
                    }
                }
                catch (generator.stop_exception)
                {
                    LogMgr.Error($"PLC {ip}:{port} 关闭");
                    throw;
                }
                finally
                {
                    mc.close();
                }
                await generator.sleep(10000);
            }
        }

        public void Close()
        {
            _mcAction.stop();
        }

        public Task WaitClose()
        {
            return generator.stop_other(_mcAction);
        }

        public async Task<ushort[]> Reads(int addr, int count)
        {
            try
            {
                return await generator.csp_timed_invoke(_readsCsp, mctimeout, tuple.make(addr, count));
            }
            catch (chan_exception)
            {
                LogMgr.Error($"MC读写异常");
                throw;
            }
        }

        public async Task Reads(int addr, ushort[] buf)
        {
            try
            {
                (await generator.csp_timed_invoke(_readsCspBuf, mctimeout, tuple.make(addr, buf))).check();
            }
            catch (chan_exception)
            {
                LogMgr.Error($"MC读写异常");
                throw;
            }
        }

        public async Task<uint[]> Reads32(int addr, int count)
        {
            try
            {
                return await generator.csp_timed_invoke(_readsCsp32, mctimeout, tuple.make(addr, count));
            }
            catch (chan_exception)
            {
                LogMgr.Error($"MC读写异常");
                throw;
            }
        }

        public async Task Reads32(int addr, uint[] buf)
        {
            try
            {
                (await generator.csp_timed_invoke(_readsCspBuf32, mctimeout, tuple.make(addr, buf))).check();
            }
            catch (chan_exception)
            {
                LogMgr.Error($"MC读写异常");
                throw;
            }
        }

        public async Task Writes(int addr, ushort[] data)
        {
            try
            {
                (await generator.csp_timed_invoke(_writesCsp, mctimeout, tuple.make(addr, data))).check();
            }
            catch (chan_exception)
            {
                LogMgr.Error($"MC读写异常");
                throw;
            }
        }

        public async Task Writes32(int addr, uint[] data)
        {
            try
            {
                (await generator.csp_timed_invoke(_writesCsp32, mctimeout, tuple.make(addr, data))).check();
            }
            catch (chan_exception)
            {
                LogMgr.Error($"MC读写异常");
                throw;
            }
        }

        public async Task<ushort> Read(int addr)
        {
            try
            {
                return await generator.csp_timed_invoke(_readCsp, mctimeout, addr);
            }
            catch (chan_exception)
            {
                LogMgr.Error($"MC读写异常");
                throw;
            }
        }

        public async Task<uint> Read32(int addr)
        {
            try
            {
                return await generator.csp_timed_invoke(_readCsp32, mctimeout, addr);
            }
            catch (chan_exception)
            {
                LogMgr.Error($"MC读写异常");
                throw;
            }
        }

        public async Task Write(int addr, ushort data)
        {
            try
            {
                (await generator.csp_timed_invoke(_writeCsp, mctimeout, tuple.make(addr, data))).check();
            }
            catch (chan_exception)
            {
                LogMgr.Error($"MC读写异常");
                throw;
            }
        }

        public async Task Write32(int addr, uint data)
        {
            try
            {
                (await generator.csp_timed_invoke(_writeCsp32, mctimeout, tuple.make(addr, data))).check();
            }
            catch (chan_exception)
            {
                LogMgr.Error($"MC读写异常");
                throw;
            }
        }

        public async Task Wait(int addr, ushort st, ushort rst)
        {
            while (st != await Read(addr))
            {
                await generator.sleep(1);
            }
            await Write(addr, rst);
        }

        public async Task<ushort> WaitNot(int addr, ushort st)
        {
            ushort res = 0;
            while (st == (res = await Read(addr)))
            {
                await generator.sleep(1);
            }
            await Write(addr, st);
            return res;
        }
    }
}
